input()
、output()
、viewChild()
、viewChildren()
、contentChild()
、contentChildren()
、outputFromObservable()
和 outputToObservable()
處於開發者預覽狀態 (developer preview)。因此,專家不建議在 production application 中使用它們。 Production application 應繼續使用裝飾器 (decorators),直到上述功能進入穩定狀態。
出現一個問題,開發團隊將負責遷移這些裝飾器和函數。 這些工作需要對任何規模的 Angular 專案進行規劃 (planning)、實施 (implementation) 和測試 (testing) 。
幸運的是,ngxtension
函式庫提供了幾個可以在專案中執行自動遷移的 schematics。Schematics 包括訊signal inputs、new ouputs 和 queries migrations。
讓我們在這篇文章中逐步進行遷移。
您可以在此 feature branch 中找到裝飾器 (decorator) 版本,https://github.com/railsstudent/ng-signal-migration-demo/tree/feat/decorators。
@Component({
selector: 'app-some',
standalone: true,
imports: [FormsModule],
template: `
<div>
<p>bgColor: {{ bgColor }}</p>
<p>name: {{ name }}</p>
</div>
`,
})
export class SomeComponent {
@Input({ required: true, alias: 'backgroundColor'}) bgColor!: string;
@Input({ transform: (x: string) => x.toLocaleUpperCase() }) name: string = 'input decorator';
}
SomeComponent
組件使用 Input
裝飾器接收來自父組件的輸入 (input)。 bgColor
是必要的輸入 (input),具有別名 backgroundColor
。 name
是將傳入值轉換為大寫的輸入 (input)。
export class SomeComponent {
@Output() triple = new EventEmitter<number>();
@Output('cube') powerXBy3 = new EventEmitter<number>();
numSub = new BehaviorSubject<number>(2);
@Output() double = this.numSub.pipe(map((n) => n * 2));
}
同一組件還具有三個 output 裝飾器。 triple
是一個發出數字的 event emitter。 powerXBy3
也是一個帶有別名 cube 的數字 event emitter。
double消耗
numSub` behaviorSubject 的值,乘以 2,並將結果傳送給父組件。
現在,在您選擇的任何終端機中執行 ngxtension
schematics 來執行遷移。
ng g ngxtension:convert-signal-inputs
遷移後,
bgColor = input.required<string>({ alias: 'backgroundColor' });
name = input<any, string | string>('input decorator', { transform: (x: string) => x.toLocaleUpperCase() });
對於 bgColor
,input.required 函數使用 alias 選項。對於 name
signal input,使用初始值和轉換選項來將輸入文字大寫。 對父組件沒有影響。
@Component({
selector: 'app-some',
standalone: true,
imports: [FormsModule],
template: `
<div>
<p>bgColor: {{ bgColor() }}</p>
<p>name: {{ name() }}</p>
`,
})
export class SomeComponent {}
bgColor
和 name
是 signal inputs; HTML 範本呼叫 signal 函數來顯示它們的值。
ng g ngxtension:convert-outputs
遷移後,
triple = output<number>();
powerX3 = output<number>({ alias: 'cube' });
numSub = new BehaviorSubject<number>(2);
double = outputFromObservable(this.numSub.pipe(map((n) => n * 2)));
triple
output 函數輸出數字。 powerX3
是 output 函數並將別名選項傳遞為第一個參數。別名的值為cube,與之前相同。 double
使用 outputFromObservable
函數將 Observable
轉換為 output
。對父組件沒有影響。
export class QueriesComponent implements AfterContentInit {
@ContentChild('header') header!: ElementRef<HTMLDivElement>;
@ContentChildren('p') body!: QueryList<ElementRef<HTMLParagraphElement>>;
appendHeader = '';
list = '';
ngAfterContentInit(): void {
this.appendHeader = `${this.header.nativeElement.textContent} Appended`;
this.list = this.body.map((p) => p.nativeElement.textContent).join('---');
}
}
QueriesComponent
組件會套用 ContentChild
和 ContentChildren
裝飾器 (decoratos) 來查詢投影到 <ng-content>
元素的 HTML 元素。
export class AppComponent implements AfterViewInit {
@ViewChild(QueriesComponent) queries!: QueriesComponent;
@ViewChildren('a') aComponents!: QueriesComponent[];
viewChildName = '';
numAComponents = 0;
ngAfterViewInit(): void {
this.viewChildName = this.queries.name;
this.numAComponents = this.aComponents.length;
}
}
AppComponent
組件使用 ViewChild
裝飾器來查詢第一個 QueriesComponent
。 它使用 ViewChildren
裝飾器來查詢與範本變數 (template variable) a
相符的所有 QueriesComponent
組件。
ng g ngxtension:convert-queries
遷移後,
export class QueriesComponent implements AfterContentInit {
header = contentChild<ElementRef<HTMLDivElement>>('header');
body = contentChildren<ElementRef<HTMLParagraphElement>>('p');
appendHeader = '';
list = '';
ngAfterContentInit(): void {
this.appendHeader = `${this.header()?.nativeElement.textContent} Appended`;
this.list = this.body().map((p) => p.nativeElement.textContent).join('---');
}
}
此 schematic 將 ContentChild
裝飾器遷移到具有正確類型的 contentChild
函數。同樣,schematic 將 ContentChildren
裝飾器遷移到 contentChildren
函數。 contentChild
和 contentChildren
函數傳回一個 signal;因此,ngAfterContentInit
生命週期方法中的程式碼也被修改。
export class AppComponent implements AfterViewInit {
queries = viewChild(QueriesComponent);
aComponents = viewChildren<QueriesComponent[]>('a');
ngAfterViewInit(): void {
this.viewChildName = this.queries()?.name || '';
this.numAComponents = this.aComponents().length;
}
}
此 schematic 將 ViewChild
裝飾器遷移到具有正確類型的 viewChild
函數。同樣,schematic 將 ViewChildren
裝飾器遷移到 viewChildren
函數。 viewChild
和 viewChildren
函數傳回一個 signal;因此,ngAfterViewInit
生命週期方法中的程式碼也被修改。
ngxtension:convert-signal-inputs
schematic 將 Input
裝飾器移轉到 signal input。ngextension:convert-outputs
schematic 將 Output
裝飾器遷移到 output
或outputFromObservable
函數。ngextension:convert-queries
schematic 將 ViewChild
、 ViewChildren
、ContentChild
、 ContentChildren
裝飾器遷移到 viewChild
、 viewChildren
、contentChild
和 contentChildren
函數。鐵人賽的第 26 天到此結束。